package core.algorithm.DecisionVariables;

import core.problem.FactoryProblems;
import core.tools.MyMath.RandomGenerator;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.math3.ml.distance.EuclideanDistance;

/**
 *
 * @author Administrator
 */
public class IntDecisionVariable extends AbstractDecisionVariable implements Cloneable, Comparable {

    public static int randomSeed = 4;

    public IntDecisionVariable(int[] intGenecodes) {
        this.intGenecodes = intGenecodes;
    }

    public IntDecisionVariable() {
    }

    @Override
    public boolean equals(Object obj) {
        if (null == obj || !(obj instanceof IntDecisionVariable)) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        //每个决策变量都相等，才相等;而没考虑适应值
        int i = 0;
        IntDecisionVariable tem = (IntDecisionVariable) obj;
        for (int j = 0; j < getIntGenecodes().length; j++) {
            if ((int) tem.getIntGenecodes()[j] == (int) this.getIntGenecodes()[j]) {
                i++;
            } else {
                break;
            }
        }
        return i == getIntGenecodes().length;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 61).
                append(this.getIntGenecodes()).
                toHashCode();
    }

    @Override
    public GenecodeType getGenecodeType() {
        return GenecodeType.INTGENECODE;//表示整数编码
    }

    public int getAdjusted(int value, int location, int[][] scope) {
        while (value > scope[location][0] || value < scope[location][1]) {
            if (value > scope[location][0]) {//超过了上限
                value = scope[location][0] - 1;
            }
            if (value < scope[location][1]) {//低于了下限
                value = scope[location][1] + 1;
            }
        }
        return value;
    }

    @Override
    public String getX() {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < getIntGenecodes().length; i++) {
            result.append(getIntGenecodes()[i]).append(",");
        }
        return result.substring(0, result.length() - 1);
    }

    @Override
    public String getGeneralGenecodeString() {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < geneCodes.length; i++) {
            result.append(geneCodes[i]);
        }
        return result.substring(0, result.length() - 1);
    }

    @Override
    public int getSolutionNumberDistance(DecisionVariable individual) {
        //返回传入的个体与当前个体之间有多少个个体数目
        //将被用于调节exploer和exploit的平衡。例如淘汰个体数目，对于适应值高的个体，中间个体数目应该少些，即少淘汰一些，以增加exploit；而对于适应低的个体，中间个体数目应该多些，即多淘汰一些，以增加exploer
        int result = 1;
        for (int i = 0; i < this.getIntGenecodes().length; i++) {
            //对于二维来说，是面积；3维来说，是体积；高维，超体积
            result *= Math.abs(this.getIntGenecodes()[i] - ((IntDecisionVariable) individual).getIntGenecodes()[i]);//VariableProperties()[i][2]决定了对搜索空间分割的精度precision
        }
        return result;
    }

    @Override
    public List<DecisionVariable> crossover(DecisionVariable crossed) {
        List<DecisionVariable> result = new LinkedList<>();
        if (this.equals(crossed)) {//派来交叉的编码完全一样,求助于变异
            result.add(this.mutate());
            result.add(crossed.mutate());
            return result;
        }
        //两个编码不一样
        //定义两个结果的clone
        IntDecisionVariable individual1 = (IntDecisionVariable) this.clone();
        IntDecisionVariable individual2 = (IntDecisionVariable) crossed.clone();
        //修改其基因编码
        int[][] crossresult = swapIntArray(individual1.getIntGenecodes(), individual2.getIntGenecodes());
        if (this.compareTwoIntArray(crossresult[0], this.getIntGenecodes()) == 0
                || this.compareTwoIntArray(crossresult[1], crossed.getIntGenecodes()) == 0
                || this.compareTwoIntArray(crossresult[0], crossed.getIntGenecodes()) == 0
                || this.compareTwoIntArray(crossresult[1], this.getIntGenecodes()) == 0) {//交叉没有对搜索空间的新探索，仍保持原来的解
            crossresult[0] = this.mutate().getIntGenecodes();
            crossresult[1] = this.mutate().getIntGenecodes();
        }
        individual1.setIntGenecodes(crossresult[0]);
        individual2.setIntGenecodes(crossresult[1]);
        //把结果加入
        result.add(individual1);
        result.add(individual2);
        return result;
    }

    public int[][] swapIntArray(int[] currentIntArray0, int[] currentIntArray1) {

        int intLength = currentIntArray0.length;//基因变量个数
        int[][] result = new int[2][intLength];//2表示交叉后得到两个个体
        if (intLength > 1) {
            int temInt = RandomGenerator.getRandom(0, intLength);
            if (temInt == 0) {
                temInt++;
            }
            //先拷贝前半部分
            System.arraycopy(currentIntArray0, 0, result[0], 0, temInt);
            System.arraycopy(currentIntArray1, 0, result[1], 0, temInt);
            //再拷贝后半部分
            System.arraycopy(currentIntArray0, temInt, result[1], temInt, currentIntArray0.length - temInt);
            System.arraycopy(currentIntArray1, temInt, result[0], temInt, currentIntArray1.length - temInt);//

        } else {//决策变量只有一个,//验证结果在这两个数值的中间
            int max = Math.max(currentIntArray0[0], currentIntArray1[0]);
            int min = Math.min(currentIntArray0[0], currentIntArray1[0]);
            if (max == min) {//不进行任何操作，因为两者相等
                result[0] = currentIntArray0;
                result[1] = currentIntArray1;
            } else {
                result[0][0] = min + (int) (RandomGenerator.getRandom(randomSeed));
                result[1][0] = max - (int) (RandomGenerator.getRandom(randomSeed));
            }
        }
        return result;
    }

    @Override
    public DecisionVariable mutate() {
        DecisionVariable result = this.clone();
        int[][] variableProperties = new int[FactoryProblems.currentProblem.getVariableProperties()[0].length][FactoryProblems.currentProblem.getVariableProperties()[1].length];
        for (int i = 0; i < FactoryProblems.currentProblem.getVariableProperties().length; i++) {
            for (int j = 0; j < FactoryProblems.currentProblem.getVariableProperties()[0].length; j++) {
                variableProperties[i][j] = (int) FactoryProblems.currentProblem.getVariableProperties()[i][j];
            }
        }
        if (this.getIntGenecodes().length > 2) {
            //首先确定变异的决策变量
            int[] temIntArray = new int[this.getIntGenecodes().length];//不能直接引用this.getIntGenecodes，因为那样就变成引用了
            System.arraycopy(this.getIntGenecodes(), 0, temIntArray, 0, this.getIntGenecodes().length);
            //for (int i = 0; i < mutationNumber; i++) {
            int muteLocation = (int) RandomGenerator.getRandom(0, temIntArray.length - 1);
            temIntArray[muteLocation] = this.getIntGenecodes()[muteLocation] + RandomGenerator.getRandom(randomSeed);
            for (int i = 0; i < FactoryProblems.currentProblem.getVariableProperties().length; i++) {
                temIntArray[i] = this.getAdjusted(temIntArray[i], i, variableProperties);
            }
            result.setIntGenecodes(temIntArray);//不用设置，也会改变，因为数组是引用传递
        } else {// 只有一个让第一个进行变异,如果有两个决策变量，则随机选择一个
            int[] temIntArray = new int[this.getIntGenecodes().length];//不能直接引用this.getIntGenecodes，因为那样就变成引用了
            System.arraycopy(this.getIntGenecodes(), 0, temIntArray, 0, this.getIntGenecodes().length);
            if (this.geneCodes.length == 1) {
                double rand = RandomGenerator.nextDouble();
                temIntArray[0] = this.getIntGenecodes()[0] + (int) rand * randomSeed;
                temIntArray[0] = this.getAdjusted(temIntArray[0], 0, variableProperties);
            } else {
                int i = RandomGenerator.nextDouble() < 0.5 ? 0 : 1;
                double rand = RandomGenerator.nextDouble();
                temIntArray[i] = this.getIntGenecodes()[i] + (int) (rand * randomSeed);
                temIntArray[i] = this.getAdjusted(temIntArray[i], 0, variableProperties);
            }
            result.setIntGenecodes(temIntArray);
        }
        return result;
    }

    @Override
    protected char flip(char ch) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public IntDecisionVariable initVariable() {
        IntDecisionVariable result = new IntDecisionVariable();
        double[][] randScope = FactoryProblems.currentProblem.getVariableProperties();
        int[] tem = new int[randScope.length];
        for (int j = 0; j < randScope.length; j++) {
            tem[j] = (int)RandomGenerator.getRandom(randScope[j][0], randScope[j][1]);
        }
        result.setIntGenecodes(tem);
        return result;
    }

    @Override
    public double getDistance(DecisionVariable another) {
        EuclideanDistance ed = new EuclideanDistance();
        double[] a=new double[this.getIntGenecodes().length],b=new double[this.getIntGenecodes().length];
        for (int i = 0; i < a.length; i++) {
            a[i]=this.getIntGenecodes()[i];
            b[i]=another.getIntGenecodes()[i];
        }
        return ed.compute(a, b);
    }

    @Override
    public IntDecisionVariable clone() {
        IntDecisionVariable result = new IntDecisionVariable();
        int[] localgeneCodes = new int[this.getIntGenecodes().length];
        System.arraycopy(this.getIntGenecodes(), 0, localgeneCodes, 0, this.getIntGenecodes().length);
        result.setIntGenecodes(localgeneCodes);
        if (null != getMutationScope()) {//这个主要是针对人脸上点
            double[][] localmutationScope = new double[getMutationScope().length][getMutationScope()[0].length];
            for (int i = 0; i < getMutationScope().length; i++) {
                System.arraycopy(getMutationScope()[i], 0, localmutationScope[i], 0, getMutationScope()[0].length);
            }
            result.setMutationScope(localmutationScope);
        }
        return result;
    }

    @Override
    public int compareTo(Object o) {
        return compareTwoIntArray(this.getIntGenecodes(), ((IntDecisionVariable) o).getIntGenecodes());
    }

    public int compareTwoIntArray(int[] a, int[] b) {
        for (int i = 0; i < a.length; i++) {
            if (a[i] > b[i]) {
                return 1;
            } else if (a[i] < b[i]) {
                return -1;
            }
        }
        return 0;//两者相等，是同一个解
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < geneCodes.length; i++) {
            sb.append(geneCodes[i]).append(",");
        }
        return sb.toString();
    }

    @Override
    public int[] getIntGenecodes() {
        return this.intGenecodes;
    }

    @Override
    public void setIntGenecodes(int[] intGenecodes) {
        this.intGenecodes = intGenecodes;
    }
}
